﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MathMethods;
using WinAPI32Library;

namespace Events
{
    class Program
    {
        // x, y - data from file
        // varY [newthon, lagrange, leastsquares]
        public static double[] x, y, varY = new double[3];
        // temp var for x
        public static double varX;
        // array of answers x and y
        public static List<double> ansX = new List<double>();
        public static List<double[]> ansY = new List<double[]>();
        // thread priorities for three threads
        public static int[] threadPriorities = {
                                     (int)Kernel32.ThreadPriority.THREAD_PRIORITY_HIGHEST,
                                     (int)Kernel32.ThreadPriority.THREAD_PRIORITY_NORMAL,
                                     (int)Kernel32.ThreadPriority.THREAD_PRIORITY_IDLE
                                 };

        static void Main(string[] args)
        {
            // read data from file
            ReadData();

            echo("Type q or й to exit.");

            // create three threads
            uint threadId1 = 0;
            Kernel32.LPTHREAD_START_ROUTINE newthon = new Kernel32.LPTHREAD_START_ROUTINE(Program.Newthon);
            IntPtr thread1 = Kernel32.CreateThread(0, 0, newthon, 0, (uint)Kernel32.CreationFlags.CREATE_SUSPENDED, out threadId1);

            uint threadId2 = 0;
            Kernel32.LPTHREAD_START_ROUTINE lagrange = new Kernel32.LPTHREAD_START_ROUTINE(Program.Lagrange);
            IntPtr thread2 = Kernel32.CreateThread(0, 0, lagrange, 0, (uint)Kernel32.CreationFlags.CREATE_SUSPENDED, out threadId2);

            uint threadId3 = 0;
            Kernel32.LPTHREAD_START_ROUTINE mnk = new Kernel32.LPTHREAD_START_ROUTINE(Program.LeastSquaresMetod);
            IntPtr thread3 = Kernel32.CreateThread(0, 0, mnk, 0, (uint)Kernel32.CreationFlags.CREATE_SUSPENDED, out threadId3);

            // ask user for thread priorities
            while (true)
            {
                echo("Set priorities for Newthon, Lagrange, Least squares (e.g. type: 1 2 3): ", false);
                // params from console
                string pars = Console.ReadLine();
                // check if there are three params
                if (pars.Trim(' ').Split(' ').Length != 3)
                    continue;
                else
                {
                    // set thread params
                    Kernel32.SetThreadPriority(thread1, threadPriorities[Convert.ToInt32(pars.Split(' ')[0]) - 1]);
                    Kernel32.SetThreadPriority(thread2, threadPriorities[Convert.ToInt32(pars.Split(' ')[1]) - 1]);
                    Kernel32.SetThreadPriority(thread3, threadPriorities[Convert.ToInt32(pars.Split(' ')[2]) - 1]);
                    break;
                }
            }

            // resume all threads
            Kernel32.ResumeThread(thread1);
            Kernel32.ResumeThread(thread2);
            Kernel32.ResumeThread(thread3);

            // create three mutexes for three interpolation methods
            IntPtr[] mutexesToWait = {
                                        Kernel32.CreateMutex(IntPtr.Zero, false, "Newthon"),
                                        Kernel32.CreateMutex(IntPtr.Zero, false, "Lagrange"),
                                        Kernel32.CreateMutex(IntPtr.Zero, false, "LeastSquares")
                                    };

            while (true)
            {
                // input data
                string input = "";
                echo("PLease enter x: ", false);
                // read console
                input = Console.ReadLine();
                // if user typed this, exit
                if (input == "q" || input == "й")
                {
                    // close all opened handles
                    Kernel32.CloseHandle(thread1);
                    Kernel32.CloseHandle(thread2);
                    Kernel32.CloseHandle(thread3);
                    for (int i = 0; i < 3; i++)
                        Kernel32.CloseHandle(mutexesToWait[i]);
                    // write toi console all data
                    for (int i = 0; i < ansX.Count; i++)
                    {
                        echo("y(" + ansX[i] + ") = [", false);
                        foreach (double d in ansY[i])
                            echo(d + "  ", false);
                        echo("]");
                    }
                    break;
                }
                // try to convert to double 
                try { varX = Convert.ToDouble(input); }
                catch { continue; }
                varY = new double[3];
                // release mutexes
                for (int i = 0; i < 3; i++)
                    Kernel32.ReleaseMutex(mutexesToWait[i]);
                // add x and three calculated y`s to answers array
                ansX.Add(varX);
                ansY.Add(varY);
            }
            Console.ReadKey();
        }

        public static void echo(dynamic s, bool newline = true)
        {
            if (newline)
                Console.WriteLine(s);
            else
                Console.Write(s);
        }

        public static uint Newthon(uint lpParam)
        {
            while (true)
            {
                // open mutex
                IntPtr m = Kernel32.OpenMutex((uint)Kernel32.SyncObjectAccess.MUTEX_ALL_ACCESS, true, "Newthon");
                // wait for mutex
                Kernel32.WaitForSingleObject(m, Kernel32.INFINITE);
                // calculate Newthon polnomial
                varY[0] = NumericalMethods.NewtonPolynomial(varX, x, y);
            }
            // return something
            return 0;
        }

        public static uint Lagrange(uint lpParam)
        {
            while (true)
            {
                // open mutex
                IntPtr m = Kernel32.OpenMutex((uint)Kernel32.SyncObjectAccess.MUTEX_ALL_ACCESS, true, "Lagrange");
                // wait for mutex
                Kernel32.WaitForSingleObject(m, Kernel32.INFINITE);
                // calculate Newthon polynomial
                varY[1] = NumericalMethods.LagrangePolynomial(varX, x, y);
            }
            // return something
            return 0;
        }

        public static uint LeastSquaresMetod(uint lpParam)
        {
            while (true)
            {
                // open mutex
                IntPtr m = Kernel32.OpenMutex((uint)Kernel32.SyncObjectAccess.MUTEX_ALL_ACCESS, true, "LeastSquares");
                // wait for mutex
                Kernel32.WaitForSingleObject(m, Kernel32.INFINITE);
                // максимальная длина
                int n = Math.Min(x.Length, y.Length);
                double
                    // вычисление формулы 1
                    s1 = x.Aggregate(0.0, (current, item) => current + item),
                    // вычисление формулы 2
                    s2 = y.Aggregate(0.0, (current, item) => current + item),
                    // вычисление формулы 3
                    s3 = x.Aggregate(0.0, (current, item) => current + item * item),
                    s4 = 0;
                // вычисление формулы 4
                for (int i = 0; i < n; i++)
                    s4 += x[i] * y[i];
                // вычисление коэффициентов прямой
                double a = (s4 - (s1 * s2) / n) / (s3 - s1 * s1 / n),
                       b = (s2 - a * s1) / n;
                // ввод аргумента и вычисление функции
                double
                    arg = varX,
                    func = a * arg + b;
                // write answer to answer var
                varY[2] = func;
            }
            // return something
            return 0;
        }

        public static void ReadData()
        {
            // считываем данные из файла
            StreamReader r = new StreamReader(@"D:\MSVSProjects\Erofeev\LeastSquares\bin\Debug\data.txt", System.Text.Encoding.UTF8);
            string s = "";
            List<double> xtemp = new List<double>();
            List<double> ytemp = new List<double>();

            while ((s = r.ReadLine()) != null)
            {
                xtemp.Add(Convert.ToDouble(s.Split(' ')[0]));
                ytemp.Add(Convert.ToDouble(s.Split(' ')[1]));
            }
            r.Close();
            x = xtemp.ToArray();
            y = ytemp.ToArray();
        }
    }
}
